package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.enums.DeviceLocationTypeEnum;
import com.zzyl.enums.DevicePhysicalLocationTypeEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.utils.ObjectUtil;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @Description DeviceServiceImpl
 * @Author mks
 * @Date 2024-09-12
 */
@Slf4j
@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;

    @Autowired
    private Client client;

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 从物联网平台同步产品列表
     */
    @Override
    public void syncProductList() {
        //1.远程调用IOT查询所有产品列表接口
        //构造请求对象封装请求参数（CurrentPage，PageSize，IotInstanceId）
        QueryProductListRequest request = new QueryProductListRequest();
        request.setCurrentPage(1);
        request.setPageSize(200);
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        //执行远程调用获取返回结果
        QueryProductListResponse response;

        try {
            response = client.queryProductList(request);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("同步IOT产品列表报错:{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        //判断接口调用是否成功
        if(!response.getBody().getSuccess()){
          // 没有成功，抛出异常
            log.error("同步IOT产品列表失败");
            throw new BaseException(BasicEnum.IOT_QUERY_PRODUCT_ERROR);
        }
        //2.对返回结果解析(返回数据：data.List.ProductInfo[])封装给List<ProductVo>
        List<ProductVo> productVoList = BeanUtil.copyToList(
                response.getBody().getData().getList().getProductInfo(),
                ProductVo.class);
        //3.写入缓存
        //定义key
        String key = CacheConstants.ALL_PRODUCT_KEY;
        //写入
        redisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(productVoList),24, TimeUnit.HOURS);
    }

    /**
     * 查询所有产品列表
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        // 1. 从缓存中获取产品列表数据json字符串
        String json = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        // 2. 判断json是否为空，为空则返回空集合数据
        if(StrUtil.isEmpty(json)){
            return Collections.emptyList();
        }
        // 3. json字符串有效，转换成List<ProductVo>
        List<ProductVo> productVoList = JSONUtil.toList(json, ProductVo.class);
        // 返回数据
        return productVoList;
    }

    /**
     * 注册设备
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1.判断新增的设备名称是否重复
        //1.1 根据设备名称查询指定设备数量
        Long count = deviceMapper.countByDeviceName(deviceDto.getDeviceName());
        //1.2 判断设备数据是否大于0，大于0说明存在，抛出异常注册设备失败
        if(count > 0){
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //2.调用阿里云IOT远程接口注册设备（接口名字：RegisterDevice）
        //2.1 完善请求对象IotInstanceId属性，请求对象 RegisterDeviceRequest(前端已经传递封装了 ProductKey，DeviceName，Nickname)
        RegisterDeviceRequest registerDeviceRequest = deviceDto.getRegisterDeviceRequest();
        registerDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //2.2 执行接口
        RegisterDeviceResponse registerDeviceResponse;
        try {
            registerDeviceResponse = client.registerDevice(registerDeviceRequest);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("物联网接口 - 注册设备，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //2.3 判断远程接口调用是否成功（Success属性）
        if(!registerDeviceResponse.getBody().getSuccess()){
            log.error("物联网接口 - 注册设备，调用失败");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //2.4 定义变量接收物联网设备的id
        String iotId = registerDeviceResponse.getBody().getData().getIotId();
        //3.调用阿里云IOT远程接口查询产品详情（QueryProduct）
        //3.1 封装请求对象 手动封装QueryProductRequest(ProductKey,IotInstanceId)
        QueryProductRequest queryProductRequest = new QueryProductRequest();
        queryProductRequest.setProductKey(deviceDto.getProductKey());
        queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //3.2 执行接口
        QueryProductResponse queryProductResponse;
        try {
            queryProductResponse = client.queryProduct(queryProductRequest);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("物联网接口 - 注册设备，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //3.3 判断远程接口调用是否成功（Success属性）
        if(!queryProductResponse.getBody().getSuccess()){
            log.error("物联网接口 - 注册设备，调用失败");
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //3.4 定义变量接收 产品名字
        String productName = queryProductResponse.getBody().getData().getProductName();
        //4.判断位置类型是否是绑定随身设备
        //4.1 是，设置物理位置类型为-1
        if(deviceDto.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            deviceDto.setPhysicalLocationType(-1);
        }
        //5.将设备数据插入数据库
        //
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //5.1 完善数据，设置没有门禁，为0
        device.setHaveEntranceGuard(0);
        //5.2 完善数据，封装产品名字
        device.setProductName(productName);
        //5.3 完善数据，封装设备id
        device.setIotId(iotId);
        //5.4 插入数据库
        try {
            deviceMapper.insertSelective(device);
        } catch (Exception e) {
            //5.5 插入失败（发生异常），调用阿里云IOT远程接口删除设备（DeleteDevice）
            //5.5.1 封装请求对象DeleteDeviceRequest(IotInstanceId,IotId)
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            deleteDeviceRequest.setIotId(iotId);
            try {
                // 5.5.2 执行接口
                client.deleteDevice(deleteDeviceRequest);
            } catch (Exception ex) {
                log.error("物联网接口 - 注册设备，调用失败");
                throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
            }
        }

    }

    /**
     * 分页查询设备列表
     * @param devicePageQueryDto
     * @return
     */
    @Override
    public PageResponse<DeviceVo> getByPageDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(),devicePageQueryDto.getPageSize());
        Page<DeviceVo> page = deviceMapper.selectByPage(devicePageQueryDto);
        return PageResponse.of(page,DeviceVo.class);
        //return new PageResponse(page.getTotal(),page.getResult());
    }

    /**
     * 查询设备详细数据
     * @param request
     * @return
     */
    @Override
    public DeviceVo queryDeviceDetail(QueryDeviceDetailRequest request) {
        //1.调用iot查询设备详情接口获取详情数据
        //1.1 封装请求对象（IotInstanceId，其他属性request根据前端的参数封装好了）
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //1.2 执行接口
        QueryDeviceDetailResponse response;
        try {
            response = client.queryDeviceDetail(request);
        } catch (Exception e) {
            log.error("查询设备详细数据信息报错",e.getMessage());
            throw new RuntimeException("查询设备详细数据信息失败");
        }
        //1.3 判断远程接口调用是否成功（Success属性）
        if(!response.getBody().getSuccess()){
            throw new RuntimeException("查询设备详细数据信息失败");
        }
        //1.4 获取详情数据
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData dataDetail = response.getBody().getData();
        //2.调用mapper根据设备id查询数据库设备数据DeviceVo
        DeviceVo deviceVo = deviceMapper.findByIotId(request.getIotId());
        //3.将IOT获取设备详情数据和数据库获取的DeviceVo数据合并
        BeanUtil.copyProperties(dataDetail,deviceVo, CopyOptions.create().ignoreNullValue());//dataDetail有值赋值给deviceVo，无值不要赋值
        //4.返回数据
        return deviceVo;
    }

    /**
     * 查询指定设备的物模型运行状态
     * @param request
     * @return
     */
    @Override
    public ResponseResult queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {
        //1.调用iot查询设备详情接口获取详情数据
        //1.1 封装请求对象（IotInstanceId，其他属性request根据前端的参数封装好了）
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //1.2 执行接口 (QueryDevicePropertyStatusRequest)
        QueryDevicePropertyStatusResponse response;
        try {
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            log.error("物联网接口 - 查询设备的物模型运行状态，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        //1.3 判断远程接口调用是否成功（Success属性）
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        // 获取详情数据
        QueryDevicePropertyStatusResponseBody.QueryDevicePropertyStatusResponseBodyData dataStatus = response.getBody().getData();
        // 2. 响应结果
        return ResponseResult.success(dataStatus);
    }

    /**
     * 查看指定产品的已发布物模型中的功能定义详情
     * @param request
     * @return
     */
    @Override
    public ResponseResult queryThingModelPublished(QueryThingModelPublishedRequest request) {
        //1.调用iot查询设备详情接口获取详情数据
        //1.1 封装请求对象（IotInstanceId，其他属性request根据前端的参数封装好了）
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //1.2 执行接口 (QueryThingModelPublishedResponse)
        QueryThingModelPublishedResponse response;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            log.error("物联网接口 - 查询物模型数据，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        //1.3 判断远程接口调用是否成功（Success属性）
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        // 获取详情数据
        QueryThingModelPublishedResponseBody.QueryThingModelPublishedResponseBodyData dataInfo = response.getBody().getData();
        // 2. 响应结果
        return ResponseResult.success(dataInfo);
    }

    /**
     * 更新设备
     * @param deviceDto
     */
    @Override
    public void updateDevice(DeviceDto deviceDto) {
        // 1.检验数据库是否存在设备
        Device device = deviceMapper.selectByPrimaryKey(deviceDto.getId());
        // 1.1判断查询的数据是否为空，若为空则抛出异常  ---> "该设备不存在，无法修改"
        if(ObjectUtil.isEmpty(device)){
            throw new BaseException(BasicEnum.DEVICE_NOT_EXIST);
        }

        // 2.调用阿里云IOT远程接口,更新云服务设备信息（BatchUpdateDeviceNickname）
        BatchUpdateDeviceNicknameRequest request = new BatchUpdateDeviceNicknameRequest();
        // 2.1 封装请求对象 手动封装BatchUpdateDeviceNicknameRequest(IotInstanceId)
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        /**
        *  2.2 由于前端传递过来的deviceDto与
        *   阿里云IOT远程接口(BatchUpdateDeviceNicknameRequest)的类型不符需要进行性转换
        *
        *   可以查看BatchUpdateDeviceNicknameRequest中的内部类
        *       BatchUpdateDeviceNicknameRequestDeviceNicknameInfo定义的类型
        * */
        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo nicknameInfo =
                BeanUtil.toBean(
                        deviceDto,
                        BatchUpdateDeviceNicknameRequest.BatchUpdateDeviceNicknameRequestDeviceNicknameInfo.class
                );
        request.setDeviceNicknameInfo(Lists.newArrayList(nicknameInfo));

        // 3.执行接口(BatchUpdateDeviceNicknameResponse)
        BatchUpdateDeviceNicknameResponse response;
        try {
            response = client.batchUpdateDeviceNickname(request);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("物联网接口 - 批量修改设备名称，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }
        // 3.2 判断远程接口调用是否成功（Success属性）
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_BATCH_UPDATE_DEVICE_ERROR);
        }

        // 4.将设备更新数据插入数据库
        Device device1 = BeanUtil.toBean(deviceDto, Device.class);
        // 4.1判断设备的类型，如果是随身设备，则设置物理位置是-1
        if(device1.getLocationType().equals(DeviceLocationTypeEnum.PORTABLE_DEVICE.getOrdinal())){
            device1.setPhysicalLocationType(DevicePhysicalLocationTypeEnum.ELDER.getOrdinal());
        }
        // 4.2 更新数据
        try {
            deviceMapper.updateByPrimaryKeySelective(device1);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);
        }

    }

    /**
     * 删除设备
     * @param request
     */
    @Override
    public void deleteDevice(DeleteDeviceRequest request) {
        // 1.调用iot删除设备接口
        // 1.1 封装请求对象（IotInstanceId，其他属性request根据前端的参数封装好了）
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());

        // 2 执行接口 (DeleteDeviceResponse)
        DeleteDeviceResponse response;
        try {
            response = client.deleteDevice(request);
        } catch (Exception e) {
            log.error("物联网接口 - 删除设备，调用失败,原因：{}",e.getMessage());
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }
        //2.2 判断远程接口调用是否成功（Success属性）
        if(!response.getBody().getSuccess()){
            throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
        }

        // 根据设备iotId删除设备数据
        deviceMapper.deleteByIotId(request.getIotId());

    }
}
